package com.vcredit.bird.service.ympush;

import java.util.Date;
import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.vcredit.bird.enumtype.YmPushDisplayTypeEnum;
import com.vcredit.bird.enumtype.YmPushTypeEnum;
import com.vcredit.bird.vo.ympush.AndroidBody;
import com.vcredit.bird.vo.ympush.AndroidPayload;
import com.vcredit.bird.vo.ympush.IosAlert;
import com.vcredit.bird.vo.ympush.IosBody;
import com.vcredit.bird.vo.ympush.IosPayload;
import com.vcredit.bird.vo.ympush.Notification;
import com.vcredit.bird.vo.ympush.Policy;
import com.vcredit.bird.vo.ympush.PushInputParamVo;
import com.vcredit.creditcat.util.ConcurrentDateUtil;
import com.vcredit.creditcat.util.JsonUtil;
import com.vcredit.framework.annotation.Log;
import com.vcredit.framework.util.HttpRequest;
import com.vcredit.framework.util.JacksonUtil;

@Service
public class PushService {
	
	@Value("${ympush.host}")
	private String host;
	
	@Value("${ympush.postPath}")
	private String postPath;
	
	@Value("${ympush.uploadPath}")
	private String uploadPath;
	
	@Value("${ympush.appKey}")
	private String appKey;
	
	@Value("${ympush.masterSecret}")
	private String masterSecret;
	
	@Value("${project.env}")
	private String env;
	
	/**
	 * 推送消息
	 * @author zhaoqi01
	 * @param notification
	 * @return
	 * @throws Exception
	 */
	@Log
	private boolean send(Notification notification) throws Exception {
		String url = host + postPath;
		String postBody = JsonUtil.obj2Json(notification,true);
		String sign = DigestUtils.md5Hex(("POST" + url + postBody + masterSecret).getBytes("utf8"));
		url = url + "?sign=" + sign;
		String jsonResult = HttpRequest.doJson(url, postBody);
		if("SUCCESS".equalsIgnoreCase((String)(JacksonUtil.Json2Map(jsonResult).get("ret")))) {
			return true;
		}
		return false;
	}
	
	/**
	 * 上传推送人，获得file_id
	 * @author zhaoqi01
	 * @param contents
	 * @return
	 * @throws Exception
	 */
	@Log
	private String uploadContents(String contents) throws Exception {
		String url = host + uploadPath;
		String timestamp = Integer.toString((int) (System.currentTimeMillis() / 1000));
		String postBody = JacksonUtil.keyVal2Json("appkey",appKey,"timestamp",timestamp,"content",contents);
		String sign = DigestUtils.md5Hex(("POST" + url + postBody + masterSecret).getBytes("utf8"));
		url = url + "?sign=" + sign;
		String jsonResult = HttpRequest.doJson(url, postBody);
		Map<String, Object> map = JacksonUtil.Json2Map(jsonResult);
		if(!"SUCCESS".equalsIgnoreCase((String)(map.get("ret")))) {
			return null;
		}
		Map<String,Object> map2 = (Map<String,Object>)map.get("data");
		return (String)(map2.get("file_id"));
	}
	
	public void push(PushInputParamVo param) throws Exception {
		if(param.getPushType() == 1) {
			// 全员推送
			this.broadcastPush(param);
		} else {
			// 批量推送
			this.batchPush(param);
		}
	}
	
	private void batchPush(PushInputParamVo param) throws Exception {
		if(param.getCustIds().split(",").length < 50) {
			this.customizedcastPush(param);
		} else {
			this.filecastPush(param);
		}
	}
	
	/**
	 * 自定义推送
	 * @author zhaoqi01
	 * @param param
	 * @throws Exception
	 */
	private void customizedcastPush(PushInputParamVo param) throws Exception {
		Notification.Builder builder = new Notification.Builder(appKey,YmPushTypeEnum.customizedcast.getType());
		builder.alias(param.getCustIds());
		
		this.buildAndSend(builder, param);
	}
	
	/**
	 * 文件推送
	 * @author zhaoqi01
	 * @param param
	 * @throws Exception
	 */
	private void filecastPush(PushInputParamVo param) throws Exception {
		String contents = param.getCustIds().replace(",", "\n");
		String fileId = this.uploadContents(contents);
		Notification.Builder builder = new Notification.Builder(appKey,YmPushTypeEnum.filecast.getType());
		builder.fileId(fileId);
		
		this.buildAndSend(builder, param);
	}
	
	/**
	 * 全员推送
	 * @author zhaoqi01
	 * @param param
	 * @throws Exception
	 */
	@Log
	private void broadcastPush(PushInputParamVo param) throws Exception {
		Notification.Builder builder = new Notification.Builder(appKey,YmPushTypeEnum.broadcast.getType());
		this.buildAndSend(builder, param);
	}
	
	@Log
	private void buildAndSend(Notification.Builder builder,PushInputParamVo param) throws Exception {
		String productionMode = "false";
		if("prd".equalsIgnoreCase(env)) {
			productionMode = "true";
		}
		
		String timestamp = Integer.toString((int)(System.currentTimeMillis() / 1000));
		builder.policy(this.getPolicy(param))
		.extra(param.getExtra())
		.description(param.getDesc())
		.productionMode(productionMode)
		.timestamp(timestamp);
	
		YmPushDisplayTypeEnum displayTypeEnum =  param.getDisplayType() == 0 ? 
				YmPushDisplayTypeEnum.notification : YmPushDisplayTypeEnum.message;
		AndroidBody androidBody = new AndroidBody.Builder(param.getTicker(), param.getTitle(), param.getText()).build();
		AndroidPayload androidPayload = new AndroidPayload(displayTypeEnum.getType(), androidBody);
		Notification androidNotification = builder.payload(androidPayload).build();
		
		IosBody iosBody = new IosBody(new IosAlert(param.getTicker(), 
				param.getTitle(), param.getText()), null);
		IosPayload iosPayload = new IosPayload(iosBody, param.getExtra());
		Notification iosNotification = builder.extra(null).payload(iosPayload).build();
		
		this.send(iosNotification);
		this.send(androidNotification);
	}
	
	private Policy getPolicy(PushInputParamVo param) {
		Date startTime = param.getStartTime();
		Date expireTime = param.getExpireTime();
		
		Date now = new Date();
		startTime = startTime != null && startTime.before(now) ? null : startTime;
		if(expireTime != null && (
				(startTime != null && expireTime.compareTo(startTime) != 1) || 
				expireTime.compareTo(now) != 1
			)) {
			expireTime = null;
		}
		
		String startTimeStr = null == startTime ? null : ConcurrentDateUtil.formatDateTime(startTime);
		String expireTimeStr = null == expireTime ? null : ConcurrentDateUtil.formatDateTime(expireTime);
		return new Policy(startTimeStr, expireTimeStr, null);
	}
}
